// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. See the AUTHORS file for names of contributors.
//
// An Env is an interface used by the leveldb implementation to access
// operating system functionality like the filesystem etc.  Callers
// may wish to provide a custom Env object when opening a database to
// get fine gain control; e.g., to rate limit file system operations.
//
// All Env implementations are safe for concurrent access from
// multiple threads without any external synchronization.

#ifndef STORAGE_LEVELDB_INCLUDE_ENV_H_
#define STORAGE_LEVELDB_INCLUDE_ENV_H_

#include <cstdarg>
#include <cstdint>
#include <string>
#include <vector>

#include "leveldb/export.h"
#include "leveldb/status.h"

// This workaround can be removed when leveldb::Env::DeleteFile is removed.
#if defined(__WIN32)
// On Windows, the method name DeleteFile (below) introduces the risk of
// triggering undefined behavior by exposing the compiler to different
// declarations of the Env class in different translation units.
//
// This is because <windows.h>, a fairly popular header file for Windows
// applications, defines a DeleteFile macro. So, files that include the Windows
// header before this header will contain an altered Env declaration.
//
// This workaround ensures that the compiler sees the same Env declaration,
// independently of whether <windows.h> was included.
#if defined(DeleteFile)
#undef DeleteFile
#define LEVELDB_DELETEFILE_UNDEFINED
#endif  // defined(DeleteFile)
#endif  // defined(_WIN32)

namespace leveldb {

class FileLock;
class Logger;
class RandomAccessFile;
class SequentialFile;
class Slice;
class WritableFile;

class LEVELDB_EXPORT Env {
public:
    Env();

    Env(const Env&) = delete;
    Env& operator=(const Env&) = delete;

    virtual ~Env();

    // Return a default environment suitable for the current operating
    // system.  Sophisticated users may wish to provide their own Env
    // implementation instead of relying on this default environment.
    //
    // The result of Default() belongs to leveldb and must never be deleted.
    static Env* Default();

    // Create an object that sequentially reads the file with the specified name.
    // On success, stores a pointer to the new file in *result and returns OK.
    // On failure stores nullptr in *result and returns non-OK.  If the file does
    // not exist, returns a non-OK status.  Implementations should return a
    // NotFound status when the file does not exist.
    //
    // The returned file will only be accessed by one thread at a time.
    virtual Status NewSequentialFile(const std::string& fname,
                                     SequentialFile** result) = 0;

    // Create an object supporting random-access reads from the file with the
    // specified name.  On success, stores a pointer to the new file in
    // *result and returns OK.  On failure stores nullptr in *result and
    // returns non-OK.  If the file does not exist, returns a non-OK
    // status.  Implementations should return a NotFound status when the file does
    // not exist.
    //
    // The returned file may be concurrently accessed by multiple threads.
    virtual Status NewRandomAccessFile(const std::string& fname,
                                       RandomAccessFile** result) = 0;

    // Create an object that writes to a new file with the specified
    // name.  Deletes any existing file with the same name and creates a
    // new file.  On success, stores a pointer to the new file in
    // *result and returns OK.  On failure stores nullptr in *result and
    // returns non-OK.
    //
    // The returned file will only be accessed by one thread at a time.
    virtual Status NewWritableFile(const std::string& fname,
                                   WritableFile** result) = 0;

    // Create an object that either appends to an existing file, or
    // writes to a new file (if the file does not exist to begin with).
    // On success, stores a pointer to the new file in *result and
    // returns OK.  On failure stores nullptr in *result and returns
    // non-OK.
    //
    // The returned file will only be accessed by one thread at a time.
    //
    // May return an IsNotSupportedError error if this Env does
    // not allow appending to an existing file.  Users of Env (including
    // the leveldb implementation) must be prepared to deal with
    // an Env that does not support appending.
    virtual Status NewAppendableFile(const std::string& fname,
                                     WritableFile** result);

    // Returns true iff the named file exists.
    virtual bool FileExists(const std::string& fname) = 0;

    // Store in *result the names of the children of the specified directory.
    // The names are relative to "dir".
    // Original contents of *results are dropped.
    virtual Status GetChildren(const std::string& dir,
                            std::vector<std::string>* result) = 0;

    // Delete the named file.
    //
    // The default implementation calls DeleteFile, to support legacy Env
    // implementations. Updated Env implementations must override RemoveFile and
    // ignore the existence of DeleteFile. Updated code calling into the Env API
    // must call RemoveFile instead of DeleteFile.
    //
    // A future release will remove DeleteDir and the default implementation of
    // RemoveDir.
    virtual Status RemoveFile(const std::string& fname);

    // DEPRECATED: Modern Env implementations should override RemoveFile instead.
    //
    // The default implementation calls RemoveFile, to support legacy Env user
    // code that calls this method on modern Env implementations. Modern Env user
    // code should call RemoveFile.
    //
    // A future release will remove this method.
    virtual Status DeleteFile(const std::string& fname);

    // Create the specified directory.
    virtual Status CreateDir(const std::string& dirname) = 0;

    // Delete the specified directory.
    //
    // The default implementation calls DeleteDir, to support legacy Env
    // implementations. Updated Env implementations must override RemoveDir and
    // ignore the existence of DeleteDir. Modern code calling into the Env API
    // must call RemoveDir instead of DeleteDir.
    //
    // A future release will remove DeleteDir and the default implementation of
    // RemoveDir.
    virtual Status RemoveDir(const std::string& dirname);

    // DEPRECATED: Modern Env implementations should override RemoveDir instead.
    //
    // The default implementation calls RemoveDir, to support legacy Env user
    // code that calls this method on modern Env implementations. Modern Env user
    // code should call RemoveDir.
    //
    // A future release will remove this method.
    virtual Status DeleteDir(const std::string& dirname);

    // Store the size of fname in *file_size.
    virtual Status GetFileSize(const std::string& fname, uint64_t* file_size) = 0;

    // Rename file src to target.
    virtual Status RenameFile(const std::string& src,
                              const std::string& target) = 0;
    // Lock the specified file.  Used to prevent concurrent access to
    // the same db by multiple processes.  On failure, stores nullptr in
    // *lock and returns non-OK.
    //
    // On success, stores a pointer to the object that represents the
    // acquired lock in *lock and returns OK.  The caller should call
    // UnlockFile(*lock) to release the lock.  If the process exits,
    // the lock will be automatically released.
    //
    // If somebody else already holds the lock, finishes immediately
    // with a failure.  I.e., this call does not wait for existing locks
    // to go away.
    //
    // May create the named file if it does not already exist.
    virtual Status LockFile(const std::string& fname, FileLock** lock) = 0;

    // Release the lock acquired by a previous successful call to LockFile.
    // REQUIRES: lock was returned by a successful LockFile() call
    // REQUIRES: lock has not already been unlocked.
    virtual Status UnLockFile(FileLock* lock) = 0;

    // Arrange to run "(*function)(arg)" once in a background thread.
    //
    // "function" may run in an unspecified thread.  Multiple functions
    // added to the same Env may run concurrently in different threads.
    // I.e., the caller may not assume that background work items are
    // serialized.
    virtual void Schedule(void (*function)(void* arg), void* arg) = 0;

    // Start a new thread, invoking "function(arg)" within the new thread.
    // When "function(arg)" returns, the thread will be destroyed.
    virtual void StartThread(void (*function)(void* arg), void* arg) = 0;

    // *path is set to a temporary directory that can be used for testing. It may
    // or may not have just been created. The directory may or may not differ
    // between runs of the same process, but subsequent calls will return the
    // same directory.
    virtual Status GetTestDirectory(std::string* path) = 0;

    // Create and return a log file for storing informational messages.
    virtual Status NewLogger(const std::string& fname, Logger** result) = 0;

    // Returns the number of micro-seconds since some fixed point in time. Only
    // useful for computing deltas of time.
    virtual uint64_t NowMicros() = 0;

    // Sleep/delay the thread for the prescribed number of micro-seconds.
    virtual void SleepForMicroseconds(int micros) = 0;
};

// A file abstraction for reading sequentially through a file
class LEVELDB_EXPORT SequentialFile {
public:
    SequentialFile() = default;

    SequentialFile(const SequentialFile&) = delete;
    SequentialFile& operator=(const SequentialFile&) = delete;

    virtual ~SequentialFile();

    // Read up to "n" bytes from the file.  "scratch[0..n-1]" may be
    // written by this routine.  Sets "*result" to the data that was
    // read (including if fewer than "n" bytes were successfully read).
    // May set "*result" to point at data in "scratch[0..n-1]", so
    // "scratch[0..n-1]" must be live when "*result" is used.
    // If an error was encountered, returns a non-OK status.
    //
    // REQUIRES: External synchronization
    virtual Status Read(size_t n, Slice* result, char* scratch) = 0;

    // Skip "n" bytes from the file. This is guaranteed to be no
    // slower that reading the same data, but may be faster.
    //
    // If end of file is reached, skipping will stop at the end of the
    // file, and Skip will return OK.
    //
    // REQUIRES: External synchronization
    virtual Status Skip(uint64_t n) = 0;
};

// A file abstraction for randomly reading the contents of a file.
class LEVELDB_EXPORT RandomAccessFile {
public:
    RandomAccessFile() = default;

    RandomAccessFile(const RandomAccessFile&) = delete;
    RandomAccessFile& operator=(const RandomAccessFile&) = delete;

    virtual ~RandomAccessFile();

    // Read up to "n" bytes from the file starting at "offset".
    // "scratch[0..n-1]" may be written by this routine.  Sets "*result"
    // to the data that was read (including if fewer than "n" bytes were
    // successfully read).  May set "*result" to point at data in
    // "scratch[0..n-1]", so "scratch[0..n-1]" must be live when
    // "*result" is used.  If an error was encountered, returns a non-OK
    // status.
    //
    // Safe for concurrent use by multiple threads.
    virtual Status Read(uint64_t offset, size_t n, Slice* result,
                        char* scratch) const = 0;
};

// A file abstraction for sequential writing.  The implementation
// must provide buffering since callers may append small fragments
// at a time to the file.
class LEVELDB_EXPORT WritableFile {
public:
    WritableFile() = default;

    WritableFile(const WritableFile&) = delete;
    WritableFile& operator=(const WritableFile&) = delete;

    virtual ~WritableFile();

    virtual Status Append(const Slice& data) = 0;
    virtual Status Close() = 0;
    virtual Status Flush() = 0;
    virtual Status Sync() = 0;
};

// An interface for writing log messages.
class LEVELDB_EXPORT Logger {
public:
    Logger() = default;

    Logger(const Logger&) = delete;
    Logger& operator=(const Logger&) = delete;

    virtual ~Logger();

    // Write an entry to the log file with the specified format.
    virtual void Logv(const char* format, std::va_list ap) = 0;
};

// Identifies a locked file.
class  LEVELDB_EXPORT FileLock {
public:
    FileLock() = default;

    FileLock(const FileLock&) = delete;
    FileLock& operator=(const FileLock&) = delete;

    virtual ~FileLock();
};

// Log the specified data to *info_log if info_log is non-null.
void Log(Logger* info_log, const char* format, ...)
#if defined(__GNUC__) || defined(__clang__)
    __attribute__((__format__(__printf__, 2, 3)))
#endif
    ;

// A utility routine: write "data" to the named file.
LEVELDB_EXPORT Status WriteStringToFile(Env* env, const Slice& data,
                                        const std::string& fname);

// A utility routine: read contents of named file into *data
LEVELDB_EXPORT Status ReadFileToString(Env* env, const std::string& fname,
                                       std::string* data);

// An implementation of Env that forwards all calls to another Env.
// May be useful to clients who wish to override just part of the
// functionality of another Env.
class LEVELDB_EXPORT EnvWrapper : public Env {
public:
    // Initialize an EnvWrapper that delegates all calls to *t.
    explicit EnvWrapper(Env* t) : target_(t) {}
    virtual ~EnvWrapper();

    // Return the target to which this Env forwards all calls.
    Env* target() const { return target_; }

    // The following text is boilerplate that forwards all methods to target().
    Status NewSequentialFile(const std::string& f, SequentialFile** r) override {
        return target_->NewSequentialFile(f,r);
    }
    Status NewRandomAccessFile(const std::string& f,
                               RandomAccessFile** r) override {
        return target_->NewRandomAccessFile(f, r);
    }
    Status NewWritableFile(const std::string& f, WritableFile** r) override {
        return target_->NewWritableFile(f, r);
    }
    Status NewAppendableFile(const std::string& f, WritableFile** r) override {
        return target_->NewAppendableFile(f, r);
    }
    bool FileExists(const std::string& f) override {
        return target_->FileExists(f);
    }
    Status GetChildren(const std::string& dir,
                        std::vector<std::string>* r) override {
        return target_->GetChildren(dir, r);
    }
    Status RemoveFile(const std::string& f) override {
        return target_->RemoveFile(f);
    }
    Status CreateDir(const std::string& d) override {
        return target_->CreateDir(d);
    }
    Status RemoveDir(const std::string& d) override {
        return target_->RemoveDir(d);
    }
    Status GetFileSize(const std::string& f, uint64_t* s) override {
        return target_->GetFileSize(f, s);
    }
    Status RenameFile(const std::string& s, const std::string& t) override {
        return target_->RenameFile(s, t);
    }
    Status LockFile(const std::string& f, FileLock** l) override {
        return target_->LockFile(f, l);
    }
    Status UnLockFile(FileLock * l) { return target_->UnLockFile(l); }
    void Schedule(void (*f)(void*), void* a) override {
        return target_->Schedule(f, a);
    }
    void StartThread(void (*f)(void*), void* a) override {
        return target_->StartThread(f, a);
    }
    Status GetTestDirectory(std::string* path) override {
        return target_->GetTestDirectory(path);
    }
    Status NewLogger(const std::string& fname, Logger** result) override {
        return target_->NewLogger(fname, result);
    }
    uint64_t NowMicros() override { return target_->NowMicros(); }
    void SleepForMicroseconds(int micros) override {
        target_->SleepForMicroseconds(micros);
    }

private:
    Env * target_;
};

}  // namespace leveldb

// This workaround can be removed when leveldb::Env::DeleteFile is removed.
// Redefine DeleteFile if it was undefined earlier.
#if defined(__WIN32) && defined(LEVELDB_DELETEFILE_UNDEFINED)
#if defined(UNICODE)
#define DeleteFile DeleteFileW
#else
#define DeleteFile DeleteFileA
#endif  // defined(UNICODE)
#endif  // defined(_WIN32) && defined(LEVELDB_DELETEFILE_UNDEFINED)

#endif  // STORAGE_LEVELDB_INCLUDE_ENV_H_
